package fr.ece.ing4.si.multithread;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import fr.ece.ing4.si.core.*;



public class MultiThreadMonteCarlo extends AbstractMonteCarlo{

	@Override
	public Object doInBackground() {
		return null;
	}
	
	
	@Override
	public double computeOptionPrice(final MonteCarloArgs MCArgs) throws InterruptedException, ExecutionException 
	{
		double sum = 0;
		
		final int numberOfCores = Runtime.getRuntime().availableProcessors();
	    final double blockingCoefficient = 0.9;
	    final int poolSize = (int)(numberOfCores / (1 - blockingCoefficient));
	    
	    System.out.println("Number of Cores available is " + numberOfCores);
	    System.out.println("Pool size is " + poolSize);
	    
	    final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
	    

	    for(int i=0; i<poolSize; i++) 
	    {
	        partitions.add(new Callable<Double>() {
	          public Double call() throws Exception {
	            
	        	double x = 0;
	            for (int j=0; j<(MCArgs.nSimulations/poolSize); j++)
	            {
	            	x += MonteCarloStandardOptionOneSimulation(
	                        MCArgs.flag, 
	                        MCArgs.S, 
	                        MCArgs.X, 
	                        MCArgs.T, 
	                        MCArgs.r, 
	                        MCArgs.b, 
	                        MCArgs.v,
	                        MCArgs.nSteps);
	            }
	            
	            return x;
	          }        
	        });
	     
	    }
	    
	    
	    final ExecutorService executorPool = Executors.newFixedThreadPool(poolSize);    
	    final List<Future<Double>> valueOfStocks = executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);	    
	    for(final Future<Double> valueOfAStock : valueOfStocks)  
	        sum += valueOfAStock.get(); 
	    
	    executorPool.shutdown();
	    
	    double average = sum / (double)MCArgs.nSimulations;
        
        return average;	    
	}


}
